package cn.matezk.iot;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.iotexplorer.v20190423.IotexplorerClient;
import com.tencentcloudapi.iotexplorer.v20190423.models.*;
import org.apache.log4j.Logger;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class TencentUtils {
    private static Logger logger = Logger.getLogger(App.class);

    private static final String endpoint = "iotexplorer.tencentcloudapi.com";
    private static final String region = "ap-guangzhou";
    private static final String secretId = "AKID3yiFiePuEOM9k8G9sPhkZBakfWAbppdF";
    private static final String secretKey = "Yu2ziCSQDnAVdXBalkU0VpwpTvAC0jFX";

    private static final String HMAC_ALGO = "hmacsha256";
    private static final String DECRYPT_MODE = "AES/CBC/NoPadding";

    public static String createSignature(String productId, String deviceName) throws TencentCloudSDKException {
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(endpoint);
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        Credential credential = new Credential(secretId, secretKey);
        GenSingleDeviceSignatureOfPublicRequest request = new GenSingleDeviceSignatureOfPublicRequest();
        request.setProductId(productId);
        request.setDeviceName(deviceName);
        request.setExpire(-1L);
        IotexplorerClient client = new IotexplorerClient(credential, region, clientProfile);
        GenSingleDeviceSignatureOfPublicResponse response = client.GenSingleDeviceSignatureOfPublic(request);
        DeviceSignatureInfo signInfo = response.getDeviceSignature();
        return signInfo.getDeviceSignature();
    }

    public static String getDeviceIccid(String productId, String deviceName) throws TencentCloudSDKException {
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(endpoint);
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        Credential credential = new Credential(secretId, secretKey);
        DescribeDeviceDataRequest request = new DescribeDeviceDataRequest();
        request.setProductId(productId);
        request.setDeviceName(deviceName);
        IotexplorerClient client = new IotexplorerClient(credential, region, clientProfile);
        DescribeDeviceDataResponse response = client.DescribeDeviceData(request);
        String data = response.getData();
        JSONObject obj = JSONUtil.parseObj(data);
        return obj.containsKey("iccid") ? obj.getJSONObject("iccid").getStr("Value") : "";
    }

    public static void deleteDevice(String productId, String deviceName) {
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(endpoint);
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        Credential credential = new Credential(secretId, secretKey);
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.setProductId(productId);
        request.setDeviceName(deviceName);
        request.setForceDelete(Boolean.TRUE);
        IotexplorerClient client = new IotexplorerClient(credential, region, clientProfile);
        DeleteDeviceResponse response;
        try {
            response = client.DeleteDevice(request);
            if (ObjectUtil.isEmpty(response.getResultCode())) {
                logger.info(String.format("产品: %s 设备名称: %s已从腾讯云删除", productId, deviceName));
            } else {
                logger.error(String.format("产品: %s 设备名称: %s从腾讯云删除失败: %s", productId, deviceName, response.getResultMessage()));
            }
        } catch (TencentCloudSDKException e) {
            if (!"ResourceNotFound.DeviceNotExist".equals(e.getErrorCode())) {
                logger.error(String.format("产品: %s 设备名称: %s从腾讯云删除失败: %s", productId, deviceName, e.getMessage()));
            }
        }
    }

    public static String dynamicRegister(String productId, String productKey, String deviceName) throws NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        Mac mac = Mac.getInstance(HMAC_ALGO);
        int randNum = (int)(Math.random() * ((1 << 31) - 1));
        int timestamp = (int)(System.currentTimeMillis() / 1000);
        SecretKeySpec signKey = new SecretKeySpec(productKey.getBytes(), HMAC_ALGO);
        JSONObject obj = JSONUtil.createObj()
                .set("ProductId", productId)
                .set("DeviceName", deviceName);
        String originRequest = obj.toString();
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] encodedhash = digest.digest(originRequest.getBytes(Charset.forName("UTF-8")));
        String hashedRequest = HexUtil.encodeHexStr(encodedhash);
        String signSourceStr = String.format("%s\n%s\n%s\n%s\n%s\n%d\n%d\n%s",
                "POST",
                "ap-guangzhou.gateway.tencentdevices.com",
                "/device/register",
                "",
                HMAC_ALGO,
                timestamp,
                randNum,
                hashedRequest
        );
        mac.init(signKey);
        byte[] rawHmac = mac.doFinal(signSourceStr.getBytes());
        String hmacSign = Base64.encode(rawHmac);
        HttpResponse response = new HttpRequest("https://ap-guangzhou.gateway.tencentdevices.com/device/register")
                .method(Method.POST)
                .header("Content-Type", "application/json;charset=UTF-8")
                .header("Accept", "application/json")
                .header("X-TC-Algorithm", HMAC_ALGO)
                .header("X-TC-Timestamp", String.valueOf(timestamp))
                .header("X-TC-Nonce", String.valueOf(randNum))
                .header("X-TC-Signature", hmacSign)
                .body(originRequest).execute();
        JSONObject rspObj = JSONUtil.parseObj(response.body()).getJSONObject("Response");
        String payload = rspObj.getStr("Payload");
        int length = rspObj.getInt("Len");
        SecretKeySpec skeySpec = new SecretKeySpec(productKey.substring(0, 16).getBytes(), "AES");
        Cipher cipher = Cipher.getInstance(DECRYPT_MODE);
        byte [] ivArr = new byte[cipher.getBlockSize()];
        for (int i = 0; i < 16; i++) {
            ivArr[i] = (byte)'0';
        }
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(ivArr));
        byte[] plBytes = cipher.doFinal(Base64.decode(payload));
        rspObj = JSONUtil.parseObj(new String(plBytes).substring(0, length));
        return rspObj.getStr("psk");
    }

    public static void controlDevice(String productId, String deviceName, JSONObject data) {
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(endpoint);
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        Credential credential = new Credential(secretId, secretKey);
        ControlDeviceDataRequest request = new ControlDeviceDataRequest();
        request.setProductId(productId);
        request.setDeviceName(deviceName);
        request.setData(data.toString());
        IotexplorerClient client = new IotexplorerClient(credential, region, clientProfile);
        try {
            client.ControlDeviceData(request);
        } catch (TencentCloudSDKException e) {
            e.printStackTrace();
        }
    }
}
